演算法的核心是效率。
同一個問題,或許有 10 種方法,但只有時間複雜度最優的才適合大規模資料。
然而,暴力法(Brute Force)並不總是壞事,小範圍題目時暴力是最快上手的解法。
今天會學到:
複雜度 | 名稱 | 適用演算法 | n=10³ | n=10⁵ | n=10⁷ |
---|---|---|---|---|---|
O(1) | 常數時間 | 陣列直接存取 | ✅ | ✅ | ✅ |
O(log n) | 對數時間 | 二分搜尋、平衡樹 | ✅ | ✅ | ✅ |
O(n) | 線性時間 | 單層遍歷、HashMap | ✅ | ✅ | ⚠️ |
O(n log n) | 線性對數 | 快速排序、合併排序 | ✅ | ✅ | ❌ |
O(n²) | 平方時間 | 雙層迴圈暴力法 | ✅ | ❌ | ❌ |
O(2ⁿ) | 指數時間 | 子集合列舉 | ✅(n≤20) | ❌ | ❌ |
快速判斷技巧:
暴力法就是窮舉所有可能解。
它簡單、好寫,但必須確認輸入規模小才能使用。
適用情況:
原題:
https://cses.fi/problemset/task/1069/
題意:
給定一個 DNA 序列(只包含 A/C/G/T),找出最長連續相同字元長度。
解題思路:
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
int now=1,ans=0;
for(int i=1;i<=s.size();i++){
if(i<s.size() and s[i]==s[i-1]) now++;
else{
ans=max(ans,now);
now=1;
}
}
cout<<ans<<endl;
}
時間複雜度:O(n),因為只從頭到尾遍歷一次。
原題:
https://cses.fi/problemset/task/1622
題意:
給定一個字串,輸出所有不同的排列(Permutation),並且按字典序排序。
解題思路
用途:將 [first, last) 區間的元素重新排列成字典序下一個排列。
返回值:
使用步驟:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
sort(s.begin(), s.end());
vector<string> result;
do {
result.push_back(s);
} while (next_permutation(s.begin(), s.end()));
cout << result.size() << "\n";
for (auto &str : result) cout << str << "\n";
return 0;
}
時間複雜度:O(n × n!),因為排列數量為 n!(n ≤ 8,可直接暴力)
原題:
https://leetcode.com/problems/two-sum/description/
題意:
給定一個整數陣列 nums 和一個目標值 target,找出任意兩個數字,使得它們的和等於 target。
解題思路:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (nums[i] + nums[j] == target)
return {i, j};
}
}
return {};
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> mp;
for (int i = 0; i < nums.size(); i++) {
int diff = target - nums[i];
if (mp.count(diff))
return {mp[diff], i};
mp[nums[i]] = i;
}
return {};
}
};
原題:
https://leetcode.com/problems/subsets/description/
題意:
給定一個整數陣列,輸出所有可能的子集合(包含空集合)。
解題思路:
一開始只有空集合:[[]]
每來一個數字,就把現有的每個子集複製一份,並加上這個數字
舉例:nums = [1, 2]
初始:[[]]
加入 1:[[], [1]]
加入 2:[[], [1], [2], [1, 2]]
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res = {{}};
for (int num : nums) {
int n = res.size();
for (int i = 0; i < n; i++) {
vector<int> subset = res[i]; //複製一份
subset.push_back(num); // 加上新元素
res.push_back(subset); // 加入結果
}
}
return res;
}
};